package com.government.wxpay.service.impl;


import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpException;
import cn.hutool.json.JSONUtil;
import com.government.common.core.domain.entity.SysUser;
import com.government.common.core.domain.model.LoginUser;
import com.government.common.enums.PayTypeEnum;
import com.government.common.exception.ServiceException;
import com.government.common.utils.RequestBodyUtils;
import com.government.member.domain.MemberInfo;
import com.government.member.domain.Vip;
import com.government.member.domain.VipInfo;
import com.government.member.domain.VipOrder;
import com.government.member.service.IMemberInfoService;
import com.government.member.service.VipInfoService;
import com.government.member.service.VipOrderService;
import com.government.member.service.VipService;
import com.government.member.vo.VipUpgrade;
import com.government.sequence.SequenceService;
import com.government.wxpay.bo.PaymentBO;
import com.government.wxpay.service.WxPayService;
import com.government.wxpay.vo.NativeResponseVO;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.nativepay.model.Transaction;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.Objects;

import static com.government.common.utils.SecurityUtils.getLoginUser;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class WxpayServiceImpl implements WxPayService {

    /**
     * 商户号
     */
    @Value(value = "${wx.appId}")
    public String appId;
    /**
     * 商户号
     */
    @Value(value = "${wx.merchantId}")
    public String merchantId;
    /**
     * 商户API私钥路径
     */
//    public static String privateKeyPath = "D://工作//微信证书//WXCertUtil//cert//apiclient_key.pem";

    @Value(value = "${wx.privateKeyPath}")
    private String privateKeyPath;
    /**
     * 商户证书序列号
     */
//    public static String merchantSerialNumber = "7912A25FEBB504A06E83B29D16C620BC307B3AFD";
    @Value(value = "${wx.merchantSerialNumber}")
    private String merchantSerialNumber;
    /**
     * 商户APIV3密钥
     */
//    public static String apiV3Key = "zRq0BrQoDTAIXxBWfDKF6J1lHjrbF20D";
    @Value(value = "${wx.apiV3Key}")
    private String apiV3Key;

    @Autowired
    private SequenceService sequenceService;

    @Autowired
    private VipOrderService vipOrderService;

    @Autowired
    private VipInfoService vipInfoService;

    @Autowired
    private VipService vipService;

    @Autowired
    private IMemberInfoService memberInfoService;

    /**
     * JSAPI支付下单
     *
     * @param bo 请求参数
     * @return PrepayResponse
     * @throws HttpException             发送HTTP请求失败。例如构建请求参数失败、发送请求失败、I/O错误等。包含请求信息。
     * @throws ValidationException       发送HTTP请求成功，验证微信支付返回签名失败。
     * @throws ServiceException          发送HTTP请求成功，服务返回异常。例如返回状态码小于200或大于等于300。
     * @throws MalformedMessageException 服务返回成功，content-type不为application/json、解析返回体失败。
     */
    @Override
    public PrepayWithRequestPaymentResponse prepay(PaymentBO bo) {

        LoginUser loginUser = getLoginUser();
        SysUser user = loginUser.getUser();

        // 初始化商户配置
        Config config = new RSAAutoCertificateConfig.Builder().merchantId(merchantId)
                // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                .privateKeyFromPath(privateKeyPath).merchantSerialNumber(merchantSerialNumber).apiV3Key(apiV3Key).build();

        // 初始化服务
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
        PrepayWithRequestPaymentResponse response = null;

        VipInfo vipInfo = null;
        boolean upgrade = false;
//        if (Objects.isNull(bo.getVipInfoId())) {
//            upgrade = true;
//            VipUpgrade vipUpgrade = vipOrderService.vipUpgrade();
//            vipInfo = vipUpgrade.getVipInfo();
//        }

        if (!upgrade) {
            vipInfo = vipInfoService.selectVipInfoById(bo.getVipInfoId());
            ServiceException.throwExOnCondition(Objects.isNull(vipInfo), "会员卡不存在");
        }

        VipOrder vipOrder = null;
        boolean isAdd = false;
        // 查询该用户是否存在待支付订单
        if (Objects.nonNull(bo.getVipOrderId())) {
            vipOrder = vipOrderService.selectVipOrderById(bo.getVipOrderId());
            bo.setVipInfoId(vipOrder.getVipInfoId());
            CloseOrderRequest closeOrderRequest = new CloseOrderRequest();
            closeOrderRequest.setOutTradeNo(vipOrder.getOutTradeNo());
            closeOrderRequest.setMchid(merchantId);
            service.closeOrder(closeOrderRequest);
        }
//
//        VipInfo vipInfo = vipInfoService.selectVipInfoById(bo.getVipInfoId());
//        ServiceException.throwExOnCondition(Objects.isNull(vipInfo), "会员卡不存在");

        // ... 调用接口
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal((vipInfo.getPrice().multiply(new BigDecimal("100"))).intValue());
        amount.setCurrency("CNY");
        request.setAmount(amount);
        request.setMchid(merchantId);
        request.setNotifyUrl("http://api.touce.com.cn/wx/pay/jsapi/notice");

        MemberInfo memberInfo = memberInfoService.selectMemberInfoByMemberId(user.getUserId());
        Payer payer = new Payer();
        payer.setOpenid(memberInfo.getWechatOpenId());
        request.setPayer(payer);
        request.setOutTradeNo(sequenceService.getOutTradeNo(PayTypeEnum.JSAPI));
        request.setAppid(appId);
        request.setDescription(vipInfo.getName());
        log.info("微信jsapi支付:交易订单号:{},下单请求参数:{}", request.getOutTradeNo(), JSONUtil.toJsonStr(request));
        response = service.prepayWithRequestPayment(request);
        log.info("微信jsapi支付:交易订单号:{},下单响应参数:{}", request.getOutTradeNo(), JSONUtil.toJsonStr(response));
        // 生成会员订单
        Date now = DateUtil.date();
        if (Objects.isNull(vipOrder)) {
            vipOrder = new VipOrder();
            vipOrder.setOrderCode(sequenceService.getOutTradeNo("VIP"));
            isAdd = true;
        }

        vipOrder.setVipInfoId(bo.getVipInfoId());
        vipOrder.setTime(vipInfo.getTime());
        vipOrder.setTimeUnit(vipInfo.getTimeUnit());
        vipOrder.setVipName(vipInfo.getName());
        vipOrder.setVipType(vipInfo.getType());
        vipOrder.setOrderStatus(0);
        vipOrder.setPayStatus(0);
        vipOrder.setGoodsAmount(vipInfo.getPrice());
        vipOrder.setOrderPayAmount(vipInfo.getPrice());
        vipOrder.setUserId(user.getUserId());
        vipOrder.setPayName("微信支付");
        vipOrder.setTradeType("JSAPI");
        vipOrder.setOutTradeNo(request.getOutTradeNo());
        vipOrder.setUpdateTime(now);
        vipOrder.setUpdateBy(String.valueOf(user.getUserId()));

        if (isAdd) {
            vipOrder.setCreateTime(now);
            vipOrder.setCreateBy(String.valueOf(user.getUserId()));
            vipOrderService.insertVipOrder(vipOrder);
        } else {

            vipOrderService.updateVipOrder(vipOrder);
        }
        return response;
    }

    /**
     * 扫码支付下单
     *
     * @param bo 请求参数
     * @return PrepayResponse
     * @throws HttpException             发送HTTP请求失败。例如构建请求参数失败、发送请求失败、I/O错误等。包含请求信息。
     * @throws ValidationException       发送HTTP请求成功，验证微信支付返回签名失败。
     * @throws ServiceException          发送HTTP请求成功，服务返回异常。例如返回状态码小于200或大于等于300。
     * @throws MalformedMessageException 服务返回成功，content-type不为application/json、解析返回体失败。
     */
    @Override
    public NativeResponseVO nativePrepay(PaymentBO bo) {

        LoginUser loginUser = getLoginUser();
        SysUser user = loginUser.getUser();
        VipInfo vipInfo = null;
        boolean upgrade = false;
//        if (Objects.isNull(bo.getVipInfoId())) {
//            upgrade = true;
//            VipUpgrade vipUpgrade = vipOrderService.vipUpgrade();
//            vipInfo = vipUpgrade.getVipInfo();
//        }

        if (!upgrade) {
            vipInfo = vipInfoService.selectVipInfoById(bo.getVipInfoId());
            ServiceException.throwExOnCondition(Objects.isNull(vipInfo), "会员卡不存在");
        }

        // 初始化商户配置
        Config config = new RSAAutoCertificateConfig.Builder().merchantId(merchantId)
                // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                .privateKeyFromPath(privateKeyPath).merchantSerialNumber(merchantSerialNumber).apiV3Key(apiV3Key).build();

        // 初始化服务
        NativePayService service = new NativePayService.Builder().config(config).build();
        com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse response = null;
        // ... 调用接口

        // 查询该用户是否存在待支付订单
        VipOrder vipOrder = vipOrderService.selectUnpaidVipOrderByUserId(user.getUserId(), vipInfo.getType());
        if (Objects.nonNull(vipOrder)) {
            // 如果新的支付金额和历史支付订单的钱不一样，则生成新的支付二维码
            if (vipOrder.getOrderPayAmount().compareTo(vipInfo.getPrice()) == 0) {
                NativeResponseVO nativeResponseVO = new NativeResponseVO();
                nativeResponseVO.setCodeUrl(vipOrder.getPayUrl());
                nativeResponseVO.setOrderId(vipOrder.getOrderId());
                return nativeResponseVO;
            } else {
                com.wechat.pay.java.service.payments.nativepay.model.CloseOrderRequest closeOrderRequest = new com.wechat.pay.java.service.payments.nativepay.model.CloseOrderRequest();
                closeOrderRequest.setOutTradeNo(vipOrder.getOutTradeNo());
                closeOrderRequest.setMchid(merchantId);
                service.closeOrder(closeOrderRequest);
            }
        }

        com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest request = new com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest();
        com.wechat.pay.java.service.payments.nativepay.model.Amount amount = new com.wechat.pay.java.service.payments.nativepay.model.Amount();
        amount.setTotal((vipInfo.getPrice().multiply(new BigDecimal("100"))).intValue());
        amount.setCurrency("CNY");
        request.setAmount(amount);
        request.setMchid(merchantId);
        request.setNotifyUrl("http://api.touce.com.cn/wx/pay/native/notice");
        request.setOutTradeNo(sequenceService.getOutTradeNo(PayTypeEnum.NATIVE));
        request.setAppid(appId);
        request.setDescription(vipInfo.getName());
        log.info("微信native支付:交易订单号:{},下单请求参数:{}", request.getOutTradeNo(), JSONUtil.toJsonStr(request));
        response = service.prepay(request);
        log.info("微信native支付:交易订单号:{},下单响应参数:{}", request.getOutTradeNo(), JSONUtil.toJsonStr(response));

        // 生成会员订单
        Date now = DateUtil.date();

        boolean isAdd = false;
        if (Objects.isNull(vipOrder)) {
            vipOrder = new VipOrder();
            vipOrder.setCreateTime(now);
            vipOrder.setOrderCode(sequenceService.getOutTradeNo("VIP"));
            vipOrder.setCreateBy(String.valueOf(user.getUserId()));
            isAdd = true;
        }
        vipOrder.setVipInfoId(bo.getVipInfoId());
        vipOrder.setTime(vipInfo.getTime());
        vipOrder.setTimeUnit(vipInfo.getTimeUnit());
        vipOrder.setVipName(vipInfo.getName());
        vipOrder.setVipType(vipInfo.getType());
        vipOrder.setOrderStatus(0);
        vipOrder.setPayStatus(0);
        vipOrder.setGoodsAmount(vipInfo.getPrice());
        vipOrder.setOrderPayAmount(vipInfo.getPrice());
        vipOrder.setUserId(user.getUserId());
        vipOrder.setPayName("微信支付");
        vipOrder.setTradeType("NATIVE");
        vipOrder.setOutTradeNo(request.getOutTradeNo());
        vipOrder.setUpdateTime(now);
        vipOrder.setUpdateBy(String.valueOf(user.getUserId()));
        vipOrder.setPayUrl(response.getCodeUrl());
        if (isAdd) {
            vipOrderService.insertVipOrder(vipOrder);
        } else {
            vipOrderService.updateVipOrder(vipOrder);
        }

        vipOrder.setOrderCode(sequenceService.getOutTradeNo("VIP"));
        NativeResponseVO nativeResponseVO = new NativeResponseVO();
        nativeResponseVO.setCodeUrl(response.getCodeUrl());
        nativeResponseVO.setOrderId(vipOrder.getOrderId());
        return nativeResponseVO;
    }

    /**
     * 关闭jsapi订单
     *
     * @param outTradeNo
     */
    @Override
    public void closeJsapiPrepay(String outTradeNo) {
        // 初始化商户配置
        Config config = new RSAAutoCertificateConfig.Builder().merchantId(merchantId)
                // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                .privateKeyFromPath(privateKeyPath).merchantSerialNumber(merchantSerialNumber).apiV3Key(apiV3Key).build();

        // 初始化服务
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();

        CloseOrderRequest closeOrderRequest = new CloseOrderRequest();
        closeOrderRequest.setMchid(merchantId);
        closeOrderRequest.setOutTradeNo(outTradeNo);
        service.closeOrder(closeOrderRequest);
    }

    /**
     * 关闭native订单
     *
     * @param outTradeNo
     */
    @Override
    public void closeNativePrepay(String outTradeNo) {
        // 初始化商户配置
        Config config = new RSAAutoCertificateConfig.Builder().merchantId(merchantId)
                // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                .privateKeyFromPath(privateKeyPath).merchantSerialNumber(merchantSerialNumber).apiV3Key(apiV3Key).build();

        // 初始化服务
        NativePayService service = new NativePayService.Builder().config(config).build();

        com.wechat.pay.java.service.payments.nativepay.model.CloseOrderRequest closeOrderRequest = new com.wechat.pay.java.service.payments.nativepay.model.CloseOrderRequest();
        closeOrderRequest.setMchid(merchantId);
        closeOrderRequest.setOutTradeNo(outTradeNo);
        service.closeOrder(closeOrderRequest);
    }

    @Override
    public void nativePrepayNotice(HttpServletRequest request) throws IOException {
        String wechatTimestamp = request.getHeader("Wechatpay-Timestamp");
        String wechatpayNonce = request.getHeader("Wechatpay-Nonce");
        String wechatSignature = request.getHeader("Wechatpay-Signature");
        String wechatPaySerial = request.getHeader("Wechatpay-Serial");
        BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
        String body = RequestBodyUtils.read(reader);

        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder().serialNumber(wechatPaySerial).nonce(wechatpayNonce).signature(wechatSignature).timestamp(wechatTimestamp).body(body).build();

        // 如果已经初始化了 RSAAutoCertificateConfig，可直接使用
        NotificationConfig config = new RSAAutoCertificateConfig.Builder().merchantId(merchantId).privateKeyFromPath(privateKeyPath).merchantSerialNumber(merchantSerialNumber).apiV3Key(apiV3Key).build();

        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(config);

        // 以支付通知回调为例，验签、解密并转换成 Transaction
        Transaction transaction = parser.parse(requestParam, Transaction.class);
        log.info("微信native支付回调通知参数:{}", JSONUtil.toJsonStr(transaction));

        // 修改vip订单信息
        VipOrder vipOrder = vipOrderService.selectVipOrderByOutTradeNo(transaction.getOutTradeNo());
        if (vipOrder.getPayStatus() == 1) {
            return;
        }
        vipOrder.setPaySuccessAmount(new BigDecimal(transaction.getAmount().getTotal()).divide(new BigDecimal("100")).setScale(2, RoundingMode.HALF_UP));
        vipOrder.setOrderStatus(1);
        vipOrder.setPayStatus(1);
        vipOrder.setPayTime(DateUtil.parseDateTime(transaction.getSuccessTime().substring(0, transaction.getSuccessTime().indexOf("+")).replace("T", " ")));
        vipOrder.setTradeType(transaction.getTradeType().name());
        vipOrder.setTransactionId(transaction.getTransactionId());
        vipOrderService.updateVipOrder(vipOrder);

        vipInfoHandler(vipOrder);
    }

    @Override
    public void jsapiPrepayNotice(HttpServletRequest request) throws IOException {
        String wechatTimestamp = request.getHeader("Wechatpay-Timestamp");
        String wechatpayNonce = request.getHeader("Wechatpay-Nonce");
        String wechatSignature = request.getHeader("Wechatpay-Signature");
        String wechatPaySerial = request.getHeader("Wechatpay-Serial");
        BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
        String body = RequestBodyUtils.read(reader);

        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder().serialNumber(wechatPaySerial).nonce(wechatpayNonce).signature(wechatSignature).timestamp(wechatTimestamp).body(body).build();

        // 如果已经初始化了 RSAAutoCertificateConfig，可直接使用
        NotificationConfig config = new RSAAutoCertificateConfig.Builder().merchantId(merchantId).privateKeyFromPath(privateKeyPath).merchantSerialNumber(merchantSerialNumber).apiV3Key(apiV3Key).build();

        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(config);

        // 以支付通知回调为例，验签、解密并转换成 Transaction
        com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction transaction = parser.parse(requestParam, com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction.class);
        log.info("微信native支付回调通知参数:{}", JSONUtil.toJsonStr(transaction));

        // 修改vip订单信息
        VipOrder vipOrder = vipOrderService.selectVipOrderByOutTradeNo(transaction.getOutTradeNo());
        if (vipOrder.getPayStatus() == 1) {
            return;
        }
        vipOrder.setPaySuccessAmount(new BigDecimal(transaction.getAmount().getTotal()).divide(new BigDecimal("100")).setScale(2, RoundingMode.HALF_UP));
        vipOrder.setOrderStatus(1);
        vipOrder.setPayStatus(1);
        vipOrder.setPayTime(DateUtil.parseDateTime(transaction.getSuccessTime().substring(0, transaction.getSuccessTime().indexOf("+")).replace("T", " ")));
        vipOrder.setTradeType(transaction.getTradeType().name());
        vipOrder.setTransactionId(transaction.getTransactionId());
        vipOrderService.updateVipOrder(vipOrder);

        vipInfoHandler(vipOrder);
    }

    public void vipInfoHandler(VipOrder vipOrder) {

        VipInfo vipInfo = vipInfoService.selectVipInfoById(vipOrder.getVipInfoId());

        Vip vip = vipService.selectVipByUserId(vipOrder.getUserId(), vipInfo.getType());
        DateField dateField = null;
        switch (vipInfo.getTimeUnit()) {
            case "MONTH":
                dateField = DateField.MONTH;
                break;
            case "YEAR":
                dateField = DateField.YEAR;
                break;
        }

        boolean isAdd = false;
        // 添加用户vip卡信息
        if (Objects.isNull(vip)) {
            vip = new Vip();
            vip.setCreateTime(new Date());
            isAdd = true;
            vip.setMaturityTime(DateUtil.offset(new Date(), dateField, vipInfo.getTime()));
        } else {
            vip.setMaturityTime(DateUtil.offset(vip.getMaturityTime(), dateField, vipInfo.getTime()));
        }
        vip.setUid(vipOrder.getUserId());
        vip.setVipType(vipInfo.getType());
        vip.setUpdateTime(new Date());
        if (isAdd) {
            vipService.insertVip(vip);
        } else {
            vipService.updateVip(vip);
        }
    }

    public com.wechat.pay.java.service.payments.model.Transaction nativePayStatus(String outTradeNo) {
        // 初始化商户配置
        Config config = new RSAAutoCertificateConfig.Builder().merchantId(merchantId)
                // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                .privateKeyFromPath(privateKeyPath).merchantSerialNumber(merchantSerialNumber).apiV3Key(apiV3Key).build();

        // 初始化服务
        NativePayService service = new NativePayService.Builder().config(config).build();
        // ... 调用接口
        com.wechat.pay.java.service.payments.nativepay.model.QueryOrderByOutTradeNoRequest request = new com.wechat.pay.java.service.payments.nativepay.model.QueryOrderByOutTradeNoRequest();
        request.setOutTradeNo(outTradeNo);
        request.setMchid(merchantId);
        log.info("微信native支付:交易订单号:{},状态查询请求参数:{}", outTradeNo, JSONUtil.toJsonStr(request));
        com.wechat.pay.java.service.payments.model.Transaction transaction = service.queryOrderByOutTradeNo(request);
        log.info("微信native支付:交易订单号:{},状态查询响应参数:{}", outTradeNo, JSONUtil.toJsonStr(transaction));
        return transaction;
    }

    public com.wechat.pay.java.service.payments.model.Transaction jsapiPayStatus(String outTradeNo) {

        // 初始化商户配置
        Config config = new RSAAutoCertificateConfig.Builder().merchantId(merchantId)
                // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                .privateKeyFromPath(privateKeyPath).merchantSerialNumber(merchantSerialNumber).apiV3Key(apiV3Key).build();

        // 初始化服务
        JsapiService service = new JsapiService.Builder().config(config).build();
        // ... 调用接口
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setOutTradeNo(outTradeNo);
        request.setMchid(merchantId);
        log.info("微信jsapi支付:交易订单号:{},状态查询请求参数:{}", outTradeNo, JSONUtil.toJsonStr(request));
        com.wechat.pay.java.service.payments.model.Transaction transaction = service.queryOrderByOutTradeNo(request);
        log.info("微信jsapi支付:交易订单号:{},状态查询响应参数:{}", outTradeNo, JSONUtil.toJsonStr(transaction));
        return transaction;
    }
}
